home *** CD-ROM | disk | FTP | other *** search
/ Nebula 2 / Nebula Two.iso / SourceCode / NeXTSTEPAdvantage / Plotter / PlotView.m < prev    next >
Text File  |  1995-06-12  |  9KB  |  361 lines

  1. /* 
  2.  * PlotView.m -- Implementation file for the PlotView class 
  3.  *
  4.  * You may freely copy, distribute, and reuse the code in this example.
  5.  * NeXT disclaims any warranty of any kind, expressed or implied, as to its
  6.  * fitness for any particular use.
  7.  *
  8.  */
  9.  
  10. #import "PlotView.h"
  11. #import "drawingFuncs.h"
  12. #import <objc/Storage.h>
  13. #import <appkit/NXCursor.h>
  14. #import <appkit/Application.h>
  15. #import <appkit/Window.h>
  16. #import <appkit/Cell.h>
  17. #import <appkit/Pasteboard.h>
  18. #import <NXCType.h> 
  19. #import <math.h> 
  20. #import <dpsclient/psops.h>
  21. #import <dpsclient/wraps.h>
  22. #import <stdlib.h> 
  23. #import <string.h> 
  24.  
  25. extern float getNumber(NXStream *stream);
  26. extern int getSeparator(NXStream *stream);
  27.  
  28. #define MAXNUMLENGTH 50
  29. #define DEFAULTRADIUS 1.0
  30.  
  31. @implementation PlotView
  32.  
  33. - initFrame:(const NXRect *)frameRect
  34. /*
  35.  * Initializes the new PlotView object.  First, an initFrame: message is sent to 
  36.  * super to initialize PlotView as a View.  Next, the PlotView sets its own 
  37.  * state -- that it is opaque and that the origin of its coordinate system lies in 
  38.  * the center of its area.  It then creates and initializes its associated objects,
  39.  * a Storage object, an NXCursor, and a Cell.  Finally, it loads into the Window 
  40.  * Server some PostScript procedures that it will use in drawing itself. 
  41.  */
  42. {
  43.     NXPoint    spot;
  44.     
  45.     [super initFrame:frameRect];
  46.     [self setOpaque:YES];
  47.     [self setRadius:DEFAULTRADIUS];
  48.     [self translate:floor(frame.size.width/2) :floor(frame.size.height/2)];
  49.     points = [[Storage alloc] initCount:0 elementSize:sizeof(NXPoint) description:"{ff}"];
  50.     crossCursor = [NXCursor newFromImage:[NXImage newFromSection:"cross.tiff"]];
  51.     spot.x = spot.y = 7.0;
  52.     [crossCursor setHotSpot:&spot];
  53.     /* 
  54.      * Normally, the next two message expressions would be combined, as:
  55.      *         readOut = [[Cell alloc] initTextCell:""];
  56.      * but are here separated for clarity.
  57.      */
  58.     readOut = [Cell alloc];
  59.     [readOut initTextCell:""];
  60.     [readOut setBezeled:YES];
  61.     loadPSProcedures();
  62.     return self;
  63. }
  64.  
  65.  
  66. - setDelegate:anObject
  67. /*
  68.  * Sets the PlotView's delegate instance variable to the supplied object.
  69.  */
  70. {
  71.     delegate = anObject;
  72.     return self;
  73. }
  74.  
  75.  
  76. - drawSelf:(const NXRect *)rects :(int)rectCount
  77. /*
  78.  * Draws the PlotView's background and axes.  If there are any points,
  79.  * these are drawn too.
  80.  *
  81.  * (Note:  For simplicity, although PlotView only repaints the background 
  82.  * of the update region, it redraws the entire axes.  For better performance, 
  83.  * only those parts of the axes that fall within the update region should 
  84.  * be redrawn.)   
  85.  */
  86. {
  87.     unsigned int     i;
  88.     NXPoint        *aPoint;
  89.     
  90.     if (rects == NULL) return self;
  91.  
  92.     /* 
  93.      * If we're printing, we need to load drawing procedures to the printing context.  
  94.      * This simple test is OK for a View that prints a single page.  If there 
  95.      * were multiple pages, the procedures would be loaded multiple times.  Although 
  96.      * this is inefficient, it causes no error.  Views that print multiple pages should 
  97.      * load procedures such as these in an endPrologue method (see the View spec sheet). 
  98.      */
  99.     if (NXDrawingStatus != NX_DRAWING) 
  100.         loadPSProcedures();
  101.  
  102.     /* paint visible area white then draw axes */
  103.     PSsetgray(NX_WHITE);
  104.     NXRectFill(&rects[0]);
  105.     PSsetgray(NX_DKGRAY);
  106.     drawAxes(bounds.origin.x, bounds.origin.y, bounds.size.width, bounds.size.height);
  107.  
  108.     /* now take each point and draw it */
  109.     PSsetgray(NX_BLACK);
  110.     i = [points count];
  111.     while (i--) {
  112.         aPoint = (NXPoint *)[points elementAt:i];
  113.         drawCircle(aPoint->x, aPoint->y, radius);
  114.     } 
  115.     return self;
  116. }
  117.  
  118. - clear:sender
  119. /*
  120.  * Clears the PlotView by emptying its Storage object of all points
  121.  * and then redisplaying the PlotView.  This action is taken only if
  122.  * the PlotView's state requires it.
  123.  */
  124. {
  125.     if (needsClearing) {
  126.         [points empty];
  127.         [self display];
  128.         needsClearing = NO;
  129.     }
  130.     return self;
  131. }
  132.  
  133. - sizeTo:(NXCoord)width :(NXCoord)height
  134. /*
  135.  * Ensures that whenever the PlotView is resized, the origin of its 
  136.  * coordinate system is repositioned to the center of its area.
  137.  */
  138. {
  139.     [super sizeTo:width :height];
  140.     [self setDrawOrigin:-floor(width/2) : -floor(height/2)]; 
  141.     return self;
  142. }
  143.  
  144. - registerPoint:(NXPoint *)aPoint
  145. /*
  146.  * Adds a point to the list the PlotView keeps in its Storage object. 
  147.  */
  148. {
  149.     [points addElement:aPoint];
  150.     return self;
  151. }
  152.  
  153. - mouseDown:(NXEvent *) theEvent
  154. /*
  155.  * Responds to a message the system sends whenever the user presses the mouse
  156.  * button when the cursor is over the PlotView.  The PlotView changes the 
  157.  * cursor to a cross-hairs image and then starts asking for mouse-dragged or mouse-
  158.  * up events.  As it receives mouse-dragged events, the PlotView updates the readOut
  159.  * text Cell with the cursor's coordinates.  If the user releases the mouse
  160.  * button while the cursor is over the PlotView, the PlotView registers the 
  161.  * point and then sends a message to its delegate notifying it of the new 
  162.  * point.
  163.  */
  164. {
  165.     int    looping = YES, oldMask;
  166.     NXPoint    aPoint;
  167.     NXRect    plotRect, cellRect;
  168.        char    buffer[100];
  169.  
  170.     [crossCursor set];
  171.        [self getBounds:&plotRect];
  172.      NXSetRect(&cellRect, plotRect.origin.x, plotRect.origin.y, 100.0, 20.0);
  173.  
  174.     oldMask = [window addToEventMask:NX_MOUSEDRAGGEDMASK];
  175.     [self lockFocus];
  176.     do {
  177.         aPoint = theEvent->location;
  178.         [self convertPoint:&aPoint fromView:nil];
  179.         sprintf(buffer, "(%d, %d)", (int)aPoint.x, (int)aPoint.y); 
  180.           [readOut setStringValue:buffer];
  181.           [readOut drawInside:&cellRect inView:self];
  182.         [window flushWindow];
  183.         if (theEvent->type == NX_MOUSEUP) {
  184.             /* on mouse-up, register point, inform delegate, and clean up state */
  185.             [readOut setStringValue:""];
  186.               [readOut drawInside:&cellRect inView:self];
  187.             [window flushWindow];
  188.             if (NXPointInRect(&aPoint, &plotRect)) {
  189.                 PSsetgray(NX_BLACK);
  190.                 drawCircle(aPoint.x, aPoint.y, radius);
  191.                 [window flushWindow];
  192.                 [self registerPoint:&aPoint];
  193.                 needsClearing = YES;
  194.                 if (delegate && [delegate respondsTo:@selector(plotView:pointDidChange:)])
  195.                     [delegate plotView:self pointDidChange:&aPoint];
  196.             }
  197.             looping = NO;
  198.         }    
  199.        } while (looping && (theEvent=[NXApp getNextEvent:NX_MOUSEUPMASK|NX_MOUSEDRAGGEDMASK]));
  200.     [self unlockFocus];
  201.     [window setEventMask:oldMask];
  202.     [NXArrow set];
  203.     return self;
  204. }
  205.  
  206. - setRadius:(float)aFloat
  207. /*
  208.  * Sets the value for the radius of the PlotView's points.
  209.  */
  210. {
  211.     radius = aFloat;
  212.     return self;
  213. }
  214.  
  215.  
  216. - (float)radius
  217. /*
  218.  * Returns the value for the radius of the PlotView's points.
  219.  */
  220. {
  221.     return radius;
  222. }
  223.  
  224.  
  225. - read:(NXTypedStream *)stream
  226. /*
  227.  * Unarchives the PlotView.  Initializes four of the PlotView's instance variables
  228.  * to the values stored in the stream. 
  229.  */
  230. {
  231.     [super read:stream];
  232.     delegate = NXReadObject(stream);
  233.     NXReadTypes(stream, "@@@f", &points, &crossCursor, &readOut, &radius);
  234.     return self;
  235. }
  236.  
  237. - write:(NXTypedStream *)stream
  238. /*
  239.  * Archives the PlotView by writing its important instance variables to the stream. 
  240.  */
  241. {
  242.     [super write:stream];
  243.     NXWriteObjectReference(stream, delegate);
  244.     NXWriteTypes(stream, "@@@f", &points, &crossCursor, &readOut, &radius);
  245.     return self;
  246. }
  247.  
  248. - awake
  249. /*
  250.  * Finishes initializing a PlotView that has just been unarchived (see the read: method).
  251.  */
  252. {
  253.     loadPSProcedures();
  254.     return [super awake];
  255. }
  256.  
  257.  
  258. - (const char *)inspectorName
  259. {
  260.     return "PlotViewInspector";
  261. }
  262.  
  263. - plot:sender
  264. /*
  265.  * Responds to a plot: message by asking the PlotView's delegate for a stream
  266.  * containing the points to plot.  It then extracts number pairs from the stream
  267.  * and creates NXPoint structures with them.  For each structure it creates, the 
  268.  * PlotView sends itself a registerPoint: message to add the point to its list of 
  269.  * points.  This simple parser ignores input it doesn't understand.
  270.  */
  271. {
  272.     int          c, sign, retval;
  273.     float          aNum;
  274.     NXPoint          aPoint;
  275.     NXStream    *stream;
  276.     BOOL         processedLine = NO, gotFirst = NO, gotSecond = NO;
  277.     
  278.     if (delegate && [delegate respondsTo:@selector(plotView:providePoints:)])
  279.         [delegate plotView:self providePoints:&stream];
  280.     NXSeek(stream, 0, NX_FROMSTART);
  281.     while ((c = NXGetc(stream)) != EOF){
  282.         sign = 1;
  283.         retval = getSeparator(stream);
  284.         if (retval == 1) {
  285.             c = NXGetc(stream);
  286.         } else if (retval == -1) {
  287.             break;
  288.         }
  289.         if (!NXIsDigit(c)) {
  290.             if ((c == '-') && NXIsDigit(c = NXGetc(stream))){
  291.                     sign = -1;
  292.             } else {
  293.                 while( (c != '\n') && (c != EOF)) {
  294.                     c = NXGetc(stream);
  295.                     processedLine = YES;
  296.                 }
  297.             }
  298.         }
  299.         if (c == EOF)
  300.             break;
  301.         else if (processedLine) {
  302.             processedLine = NO;
  303.             continue;
  304.         }
  305.         aNum = getNumber(stream);
  306.         if (!gotFirst) {
  307.             aPoint.x = sign * aNum;
  308.             gotFirst = YES;
  309.         } else if (!gotSecond) {
  310.             aPoint.y = sign * aNum;
  311.             [self registerPoint:&aPoint];
  312.             gotFirst = gotSecond = NO;
  313.         } 
  314.     }
  315.     [self display];
  316.     needsClearing = YES;
  317.     return self;
  318. }
  319.  
  320. int getSeparator(NXStream *stream)
  321. /* 
  322.  * Removes white space, commas, and plus signs from between 
  323.  * number pairs.
  324.  */
  325. {
  326.     int c, firstChar;
  327.     
  328.     NXUngetc(stream);
  329.     c = firstChar = NXGetc(stream);
  330.     while (NXIsSpace(c) || (c == ',') || (c == '+'))
  331.         c = NXGetc(stream);
  332.     /* 1 = ate something; 0 = ate nothing; -1 means EOF */
  333.     if (c == firstChar)
  334.         return 0;
  335.     else if (c == EOF)
  336.         return -1;
  337.     NXUngetc(stream);
  338.     return 1;
  339. }
  340.  
  341. float getNumber(NXStream *stream)
  342. /* 
  343.  * Composes a floating point number from a string of number  
  344.  * characters.
  345.  */
  346. {
  347.     int c, i = 0;
  348.     char temp[MAXNUMLENGTH];
  349.     
  350.     NXUngetc(stream);
  351.     c = NXGetc(stream);
  352.     while ((NXIsDigit(c) || (c == '.')) && (c != '\n') && (c != EOF)) {
  353.         temp[i++] = c;
  354.         c = NXGetc(stream);
  355.     }
  356.     NXUngetc(stream);
  357.     temp[i] = 0;
  358.     return (atof(temp));
  359. }
  360.  
  361. @end